package com.alkalinesolutions.json.core.impl;

import java.io.Serializable;
import java.util.EventObject;

import com.alkalinesolutions.json.core.Messages;
import com.alkalinesolutions.json.core.UnsupportedTypeException;

public final class JSONEvent extends EventObject {
	private static final long serialVersionUID = 1L;

	public static enum EventType {
		Null, Boolean, String, Number,

		// Types for walking structures
		StartArray, EndArray,
		StartObject, EndObject
	}

	private final String key;
	private final EventType type;
	private final Serializable data;

	public JSONEvent(final Object source, final EventType type, final Serializable data,
			final String key) throws UnsupportedTypeException {
		super(source);
		if (type == null) throw new IllegalArgumentException("type"); //$NON-NLS-1$
		this.type = type;
		this.data = data;
		this.key = key;
		
		if (data != null && data instanceof Number)
			validateNumber((Number)data);
	}

	@SuppressWarnings("boxing")
	private static void validateNumber(Number data) throws UnsupportedTypeException {
		double d = data.doubleValue();
		if (Double.isInfinite(d) || Double.isNaN(d))
			throw new UnsupportedTypeException(
				String.format(Messages.getString(
						"JSONEvent.illegal-floating-point"),d)); //$NON-NLS-1$		
	}
	public EventType getType() {
		return type;
	}

	public Serializable getData() {
		return data;
	}

	public String getKey() {
		return key;
	}
	
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append('[').append(type);
		if (key != null)
			builder.append(" key=").append(key); //$NON-NLS-1$
		if (data != null)
			builder.append(" data=").append(data); //$NON-NLS-1$
		builder.append(']');
		return builder.toString();
	}

	public static JSONEvent fromData(Object source, Serializable data)
		throws UnsupportedTypeException {
		switch (JSONType.valueFromObject(data)) {
		case Boolean:
			return new JSONEvent(source, EventType.Boolean, data, null);
		case String:
			return new JSONEvent(source, EventType.String, data, null);
		case Null:
			return new JSONEvent(source, EventType.Null, null, null);
		case Number:
			return new JSONEvent(source, EventType.Number, data, null);
		default:
			throw new UnsupportedTypeException(String.format(Messages.getString("Type.unknown-type"), data.getClass().getName())); //$NON-NLS-1$
		}
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null || !(obj instanceof JSONEvent))
			return false;
		JSONEvent rhs = (JSONEvent) obj;
		
		boolean keysMatch = false;
		if (this.key == null)
			keysMatch = (rhs.key == null);
		keysMatch = this.key.equals(rhs.key);
		
		return keysMatch && 
			this.source.equals(rhs.source) && 
			this.data.equals(rhs.data) &&
			this.type.equals(rhs.type);	
	}

	@Override
	public int hashCode() {
		int keyHash = key != null ? key.hashCode() : 0;
		int dataHash = data != null ? data.hashCode() : 0;
		return source.hashCode() ^ type.hashCode() ^ dataHash ^ keyHash;
	}
	
}